package se.sics.drsm.experiments;

import java.util.Random;

import se.sics.drsm.simulation.commands.ChurnEvent;
import se.sics.drsm.simulation.commands.PeerFail;
import se.sics.drsm.simulation.commands.PeerJoin;
import se.sics.kompics.p2p.experiment.dsl.SimulationScenario;
import se.sics.kompics.p2p.experiment.dsl.adaptor.Operation;

@SuppressWarnings({"serial"})
public class SimulationTests {

    static long SEED = Long.parseLong(System.getProperty("cats.seed", "0"));
    private static Random idGenerator = new Random(System.currentTimeMillis());

    public static SimulationScenario expMixedScenario(final String s,
                                                      final long time, final long terminationTime) {
        //
        // char[] c = s.toCharArray();
        // int joinCount = 0, failCount = 0, nodesCount = 0;
        // for (int i = 0; i < c.length; i++) {
        // if (c[i] == 'J') {
        // joinCount++;
        // } else if (c[i] == 'F') {
        // failCount++;
        // nodesCount++;
        // } else {
        // nodesCount += Integer.parseInt(""+c[i]);
        // }
        // }
        //
        // final long nodes[] = new long[nodesCount];
        // final long join[] = new long[joinCount];
        // final long fail[] = new long[failCount];
        //
        // System.setProperty("bootstrap.nodes.threshold", "" + nodesCount);
        // System.setProperty("cats.experiment.scenario", s);
        //
        // for (int i = 0, kn = 0, kj = 0, kf = 0, kc = 0; i < c.length; i++) {
        //
        // if (c[i] == 'J') {
        // join[kj++] = 5 * (kc + 2);
        // kc++;
        // } else if (c[i] == 'F') {
        // fail[kf++] = 5 * (kc + 2);
        // nodes[kn++] = 5 * (kc + 2);
        // kc++;
        // } else { // assuming its a single non-zero digit, i.e. 1-9
        // int fill = Integer.parseInt(""+c[i]);
        // if (fill > 0) {
        // for(int j=0; j<fill; j++) {
        // nodes[kn++] = 5 * (kc + 2);
        // kc++;
        // }
        // }
        // }
        // }

        final long nodeIds[] = {10, 20, 30, 40, 50, 60, 100, 120, 140, 160,
                180, 200, 220, 240, 260, 280, 300,
                310, 320, 330, 340, 350, 360,
                380, 400, 420, 440, 460, 480, 500,
                580, 600, 620, 640, 660, 680, 700};

        // ok, join and fail nodes
        SimulationScenario testScenario = new SimulationScenario() {
            {

                StochasticProcess joins = new StochasticProcess() {
                    {
                        eventInterArrivalTime(constant(1500));
                        raise(10, Joins(nodeIds));
                    }
                };

                StochasticProcess fails = new StochasticProcess() {
                    {
                        eventInterArrivalTime(constant(100));
                        raise(3, Crashes(nodeIds));
                    }
                };

                StochasticProcess churns = new StochasticProcess() {
                    {
                        eventInterArrivalTime(constant(0));
                        raise(20 + 10, Crashes(nodeIds));
                    }
                };

//				StochasticProcess earlyterminator = new StochasticProcess() {
//					{
//						eventInterArrivalTime(constant(500));
//						raise(Integer.MAX_VALUE,
//								doValidation(Validation.VAL_CONVERGENCE, false));
//						// CatsValidator.VAL_SUCC^CatsValidator.VAL_SUCC_LISTS
//						// for checking both succs and succlists, and so on
//					}
//				};

                joins.start();
                fails.startAfterTerminationOf(time, joins);
                churns.startAtSameTimeWith(fails);
                terminateAfterTerminationOf(terminationTime, churns);
            }
        };

        testScenario.setSeed(SEED);

        return testScenario;
    }

    // operations

    // static Operation<CatsChurnEvent> catsChurn(final long[] join, final
    // long[] fail) {
    // return new Operation<CatsChurnEvent>() {
    // private int i = 0;
    // public CatsChurnEvent generate() {
    // if (i < join.length )
    // return new CatsPeerJoin(join[i++]);
    // else
    // return new CatsPeerFail(fail[i++ - join.length]);
    // }
    // };
    // };

    static Operation<PeerJoin> Joins(final long[] ids) {
        return new Operation<PeerJoin>() {

            private int i = 0;

            public PeerJoin generate() {
                return new PeerJoin(ids[i++]);
            }
        };
    }

    static Operation<PeerFail> Crashes(final long[] ids) {
        return new Operation<PeerFail>() {

            private int i = 0;

            public PeerFail generate() {
                return new PeerFail(ids[i++]);
            }
        };
    }

    static Operation<ChurnEvent> Churns(final int totalJoin, final long[] ids) {
        return new Operation<ChurnEvent>() {

            private int i = 0;

            public ChurnEvent generate() {
                if (i < totalJoin)
                    return new PeerJoin(ids[i++]);
                else
                    return new PeerFail(ids[i--]);
            }
        };
    }

    /**
     * @return long Randomly generate long node ids
     */
    private static long getRandomIds() {

        return idGenerator.nextLong();
    }

    /**
     * Do a validation on the snapshot
     *
     * @param detailLevel
     *            level of validation you want to do. For Cats, you can use
     *            details given in CatsValidator You can use xor to get a
     *            combination.
     * @param printErrorMessages
     *            Do you want to print error messages as to why the pointer
     *            during validation were wrong?
     * @return
     */
//	public static Operation<Validation> doValidation(final int detailLevel,
//			final boolean printErrorMessages) {
//		return new Operation<Validation>() {
//			public Validation generate() {
//				return new Validation(detailLevel, printErrorMessages);
//			}
//		};
//	}
}
